#!/usr/bin/env python
# -*- coding: UTF-8 -*-
"""
# 作者：肖银皓
# 创建：2021-06-12
# 用意：利用opencv检测出瓷砖裂缝
# 注意事项：访问http://192.168.0.100/DefectDetection/report/Report.html查看结果
"""
import math
import os
import time
import cv2
import numpy as np
import copy
from GenReport import BeautifulReport
import datetime
import warnings
import Utils
from multiprocessing import Process
warnings.filterwarnings("ignore")

beginTime = datetime.datetime.now() # 开始时间
standard_height = 1024 # 将图片转换成标准高度
standard_width = 1024  # 将图片转换成标准宽度
small_height = 480
small_width = 480
small_dim = (small_height, small_width)
sigma = 0.33  # Canny Edge计算阈值系数
point_dist_thresh = 10 # 如果两个点横纵坐标都小于这个值，则认为这两点相邻
gradient_dist_min_thresh = 1 # 如果两个相邻的斜率大于这个值，则认为可能是出现裂痕的地方
gradient_dist_avg_thresh = 0.5 # 如果大于这个值，则认为图片中存在裂痕
HoughThresh = 5 # 霍夫变换可能出现线条投票阈值
standard_dim = (standard_height, standard_width)
inf = 9999
warning_delay = 2 # 如果产品有缺陷，亮红灯响蜂鸣器持续的时间（秒）
pass_dalay = 2 # 如果产品没有缺陷，亮白灯持续的时间（秒）

img_path = "./1.jpg" # 检测图片存放路径
report_path = "./report/" # 报告结果存放路径
template_path = "./report/template.html" # 测试报告模板存放路径

if __name__ == '__main__':
    # 在另一条线程中运行摄像头
    camera_p = Process(target=Utils.RunProcess)
    camera_p.start()

    # 先删除之前的照片
    try:
        os.remove(img_path)
    except OSError:
        pass

    # os.system("xset s reset") # 唤起树莓派屏幕

    # 一直运行主程序
    while True:
        # 如果照片不存在，一直循环
        while not os.path.isfile(img_path):
            time.sleep(0.1)

        # 读取照片，并且做边界识别
        img_mat = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE)
        if img_mat is None:
            print("照片损坏，请重新拍照")
            continue
        img_mat_color = cv2.imread(img_path)
        v = np.median(img_mat)
        lower_thresh = int(max(0, (1.0 - sigma) * v))
        upper_thresh = int(min(255, (1.0 + sigma) * v))
        img_mat = cv2.resize(img_mat, standard_dim, interpolation=cv2.INTER_AREA)
        img_mat_color = cv2.resize(img_mat_color, standard_dim, interpolation=cv2.INTER_AREA)
        img_cp = copy.copy(img_mat_color)
        edges = cv2.Canny(img_mat, lower_thresh, upper_thresh) # 用Canny Edge做边界识别
        linesP = cv2.HoughLinesP(edges, 1, np.pi / 180, HoughThresh) # 霍夫变换找出所有可能线段

        line_segs = [] # 存放所有线段
        if linesP is not None:
            for i in range(0, len(linesP)):
                l = linesP[i][0]
                x1 = l[0]
                x2 = l[2]
                y1 = l[1]
                y2 = l[3]
                if x1 == x2 and y1 == y2: # 这种情况是一个单点，忽略
                    continue
                line_segs.append([x1, y1, x2, y2])

        # 对每两个相邻的线段求标准化欧几里得距离
        count = 0
        total_dist = 0
        avg_gradient_dist = 0
        for i in range(0, len(line_segs)):
            for j in range(i+1, len(line_segs)):
                x11 = line_segs[i][0]
                y11 = line_segs[i][1]
                x12 = line_segs[i][2]
                y12 = line_segs[i][3]

                x21 = line_segs[j][0]
                y21 = line_segs[j][1]
                x22 = line_segs[j][2]
                y22 = line_segs[j][3]
                # if (abs(x11 - x21) < point_dist_thresh and abs(y11-y21) < point_dist_thresh) \
                #     or (abs(x11 - x22) < point_dist_thresh and abs(y11 - y22) < point_dist_thresh) \
                #     or (abs(x12 - x21) < point_dist_thresh and abs(y12 - y21) < point_dist_thresh) \
                #     or (abs(x12 - x22) < point_dist_thresh and abs(y12 - y22) < point_dist_thresh):
                if  (abs(x12 - x21) < point_dist_thresh and abs(y12 - y21) < point_dist_thresh) \
                        or (abs(x12 - x22) < point_dist_thresh and abs(y12 - y22) < point_dist_thresh):
                    count += 1
                    div_factor = x12 - x11
                    if x12 == x11:
                        div_factor = inf
                    gradient1 = float(y12 - y11)/div_factor
                    div_factor = x22 - x21
                    if x22 == x21:
                        div_factor = inf
                    gradient2 = float(y22 - y21)/div_factor
                    gradient_dist = math.sqrt(abs(gradient1 ** 2 - gradient2 ** 2))
                    total_dist += gradient_dist

                    # 画出可能出现裂缝的地方
                    if gradient_dist > gradient_dist_min_thresh:
                        cv2.line(img_cp, (x11, y11), (x12, y12), (255, 0, 255), 3)

        if count != 0:
            avg_gradient_dist = float(total_dist) / count

        if avg_gradient_dist > gradient_dist_avg_thresh:
            print("此产品可能存在缺陷！")
            img_cp = cv2.resize(img_cp, small_dim, interpolation=cv2.INTER_AREA)
            img_mat_color = cv2.resize(img_mat_color, small_dim, interpolation=cv2.INTER_AREA)
            cv2.imwrite(report_path + "1.jpg", img_mat_color)
            cv2.imwrite(report_path + "2.jpg", img_cp)
            # while True:
            #     cv2.imshow("Original Picture", img_mat_color)
            #     cv2.imshow("Possible Defects", img_cp)
            #     if cv2.waitKey(1) & 0xFF == ord('q'):
            #         break
            testResult = "此产品可能存在异常缺陷！"
            Utils.TurnOnRed()
            Utils.TurnOnBeep()
            time.sleep(warning_delay)
            Utils.TurnOffRed()
            Utils.TurnOffBeep()

        else:
            img_mat_color = cv2.resize(img_mat_color, small_dim, interpolation=cv2.INTER_AREA)
            cv2.imwrite(report_path + "1.jpg", img_mat_color)
            try:
                os.remove(report_path + "2.jpg")
            except OSError:
                pass
            print("未检查出缺陷！")
            testResult = "未检查出异常缺陷！"
            Utils.TurnOnWhite()
            time.sleep(pass_dalay)
            Utils.TurnOffWhite()

        ##### 生成测试报告 #####
        endTime = datetime.datetime.now() # 结束时间
        FIELDS = {
            "testName": "瓷砖裂痕检测",
            "testDuration": str(endTime - beginTime),
            "beginTime": str(beginTime),
            "testResult": testResult
        }

        print("正在创建报告......")
        beautifulReport = BeautifulReport()
        beautifulReport.set_fields(FIELDS)
        beautifulReport.report(filename='Report.html', description="test", log_path=report_path, report_temp=template_path)
        print("报告创建结束！")
        ##########################

        # 先删除之前的照片
        try:
            os.remove(img_path)
        except OSError:
            pass

        # os.system("xset s reset")  # 唤起树莓派屏幕